package com.xxmw.transaction.service.app.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxmw.transaction.common.entity.P;
import com.xxmw.transaction.common.excep.service.TransactionException;
import com.xxmw.transaction.common.utils.Constant;
import com.xxmw.transaction.common.utils.bourse.*;
import com.xxmw.transaction.enumeration.TradeTypeEnum;
import com.xxmw.transaction.mapper.brick.BrickOrderMapper;
import com.xxmw.transaction.model.app.*;
import com.xxmw.transaction.model.brick.Brick;
import com.xxmw.transaction.model.brick.BrickOrder;
import com.xxmw.transaction.service.app.*;
import com.xxmw.transaction.vo.BrickOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * @author luowenliang
 * @since 2020/2/17 15:52
 */
@Slf4j
@Service
public class BrickOrderServiceImpl extends ServiceImpl<BrickOrderMapper, BrickOrder> implements BrickOrderService {

	private final ExecutorService executorService;

	@Autowired
	private SymbolSubService symbolSubService;

	@Autowired
    private BrickService brickService;

	@Autowired
	private UserBrickService userBrickService;

	@Autowired
	private SymbolService symbolService;

	@Autowired
	private UserService userService;

	public BrickOrderServiceImpl(@Autowired @Qualifier(value = "symbolServiceThreadPool")ThreadPoolExecutorFactoryBean threadPoolExecutorFactoryBean) {
		super();
		executorService = threadPoolExecutorFactoryBean.getObject();
	}

    @Override
    public List<BrickOrder> getOrderByStatus(Integer... status) {
        QueryWrapper<BrickOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("status", status);
        return list(queryWrapper);
    }

    @Autowired
    private BourseApiService bourseApiService;

    @Override
    public P<BrickOrder> pageList(Integer pageNum, Integer pageSize, Long orderNo, Integer bourse,
								  Integer status, String outId, String username, Integer brickId) {
        QueryWrapper<BrickOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("created");
        queryWrapper.eq(StringUtils.isNotBlank(outId), "out_id", outId);
        queryWrapper.eq(null != orderNo, "order_no", orderNo);
        queryWrapper.eq(null != bourse, "bourse", bourse);
        queryWrapper.eq(null != status, "status", status);
        queryWrapper.eq(null != username, "username", username);
        queryWrapper.eq(null != brickId,"brick_id", brickId);
        IPage<BrickOrder> page = new Page<>(pageNum, pageSize);
        page(page, queryWrapper);
        return new P<>(page.getTotal(), page.getRecords());
    }

    @Override
    public void place(BrickOrder bourseOrder) {
        BourseApi bourseApi = bourseApiService.getByUserBrick(bourseOrder.getBrickId(), bourseOrder.getBourse());
        if (null == bourseApi) {
        	log.error("用户" + bourseOrder.getUsername() + "未配置交易所API,下单失败");
            throw new TransactionException("用户" + bourseOrder.getUsername() + "未配置交易所API");
        }
        switch (bourseOrder.getBourse()) {
            case 1:
                try {
                    HuobiUtil.order(bourseApi, bourseOrder);
                } catch (Exception e) {
                    log.error("搬砖挂单失败：", e);
                }
                break;
            case 2:
                try {
                    BinanceUtil.order(bourseApi, bourseOrder);
                } catch (Exception e) {
                    log.error("搬砖挂单失败：", e);
                }
                break;
            case 3:
                try {
                    OkexUtil.order(bourseApi, bourseOrder);
                } catch (Exception e) {
                    log.error("搬砖挂单失败：", e);
                }
                break;
            case 4:
                try {
                    BitforexUtil.order(bourseApi, bourseOrder);
                } catch (Exception e) {
                    log.error("搬砖挂单失败：", e);
                }
                break;
			case 5:
				try {
					ZBUtil.order(bourseApi, bourseOrder);
				} catch (Exception e) {
					log.error("搬砖挂单失败：", e);
				}
				break;
			case 6:
				try {
					PoloniexUtil.order(bourseApi, bourseOrder);
				} catch (Exception e) {
					log.error("搬砖挂单失败：", e);
				}
				break;
			case 7:
				try {
					JiuBiUtil.order(bourseApi, bourseOrder);
				} catch (Exception e) {
					log.error("搬砖挂单失败：", e);
				}
				break;
			case 8:
				try {
					BikiUtil.order(bourseApi, bourseOrder);
				} catch (Exception e) {
					log.error("搬砖挂单失败：", e);
				}
				break;
            default:
                log.error("未知的交易所标识：" + bourseOrder.getBourse());
                throw new TransactionException("交易所有误");
        }
        // 记录订单
		// bourseOrder.setStatus(BourseOrder.Status.DEALED.value());
		if(bourseOrder.getStatus().equals(BourseOrder.Status.PUBLISH_ERROR.value())) {
			throw new TransactionException("下单失败");
		}
        save(bourseOrder);
    }

	@Override
	public void watch(Hedging message) {
		String symbol = message.getSymbol();
		//获取该交易对有效策略
		List<Brick> brickList = brickService.getAvailableSymbol(symbol);
		if (null == brickList || brickList.size() <= 0) {
			log.error("系统暂未添加有效搬砖策略");
			return;
		}

		Symbol baseSymbol = symbolService.getBuyName(symbol);
		String baseCurrency = baseSymbol.getBaseCurrency();
		String quoteCurrency = baseSymbol.getQuoteCurrency();

		//当前利润率
		brickList.forEach(brick -> {
			//总价格
			BigDecimal totalPrice = message.getBuyPrice().add(message.getSellPrice()).multiply(brick.getTradeNum());
			//手续费
			BigDecimal fee = totalPrice.multiply(new BigDecimal(Constant.BRICK_FEE));
			//净利润
			BigDecimal netProfit = message.getSellPrice().subtract(message.getBuyPrice()).abs().multiply(brick.getTradeNum()).subtract(fee);
			//利润率
			BigDecimal profitMargin = netProfit.divide(totalPrice, 4, BigDecimal.ROUND_HALF_UP);

			// log.error("当前利润率：{}", profitMargin);

			//净率润大于设置的最小值才去搬砖
			if (profitMargin.compareTo(brick.getMinProfitMargin()) != -1) {
				//获取使用该策略的所有需要搬砖的用户
				List<UserBrick> userBrickList = userBrickService.findByBrickId(brick.getId());
				if (null != userBrickList && userBrickList.size() > 0) {
					for (UserBrick userBrick : userBrickList) {
						//验证用户是否支持当前两交易所的搬砖
						String[] bourseIdArray = userBrick.getBourseIds().split(",");
						List<String> bourseList = Arrays.asList(bourseIdArray);
						if (bourseList.indexOf(message.getBuyBourse()) > -1 && bourseList.indexOf(message.getSellBourse()) > -1) {
							User u = userService.getById(userBrick.getUserId());
							//支持给用户下单
							executorService.execute(() -> {

								long orderNo = sequence.nextId();

								BrickOrder sellOrder = new BrickOrder();
								sellOrder.setOrderNo(orderNo);
								sellOrder.setTradeType(TradeTypeEnum.SELL_MARKET.getValue());
								sellOrder.setBourse(message.getBuyBourse());
								sellOrder.setNum(brick.getTradeNum());
								sellOrder.setSymbol(symbol);
								sellOrder.setQuoteCurrency(quoteCurrency);
								sellOrder.setBaseCurrency(baseCurrency);
								sellOrder.setPrice(message.getBuyPrice());
								sellOrder.setTotalPrice(sellOrder.getPrice().multiply(sellOrder.getNum()));
								sellOrder.setStatus(0);
								sellOrder.setCreated(new Date());
								sellOrder.setBid(message.getBuyPrice());
								sellOrder.setData(message.getBuyData());
			        			sellOrder.setUsername(u.getUsername());
			        			sellOrder.setProfit(netProfit);
								place(sellOrder);

								//市价买
								BrickOrder order = new BrickOrder();
								order.setOrderNo(orderNo);
								order.setTradeType(TradeTypeEnum.BUY_MARKET.getValue());
								order.setBourse(message.getSellBourse());
								order.setNum(brick.getTradeNum());
								order.setSymbol(symbol);
								order.setQuoteCurrency(quoteCurrency);
								order.setBaseCurrency(baseCurrency);
								order.setPrice(message.getSellPrice());
								order.setTotalPrice(order.getPrice().multiply(order.getNum()));
								order.setStatus(0);
								order.setCreated(new Date());
								order.setAsk(message.getSellPrice());
								order.setData(message.getSellData());
			        			order.setUsername(u.getUsername());
								sellOrder.setProfit(netProfit);
								place(order);
							});
						}
					}
				}
			}
		});
	}

	@Override
	public void preOrder(Hedging hedging) {
		String symbol = hedging.getSymbol();
		//获取该交易对有效策略
		List<Brick> brickList = brickService.getAvailableSymbol(symbol);
		if (null == brickList || brickList.size() <= 0) {
			log.error("系统暂未添加有效搬砖策略");
			return;
		}

		Symbol baseSymbol = symbolService.getBuyName(symbol);
		String baseCurrency = baseSymbol.getBaseCurrency();
		String quoteCurrency = baseSymbol.getQuoteCurrency();

		//总价格 = 买卖价格之和
		BigDecimal totalPrice = hedging.getBuyPrice().add(hedging.getSellPrice());
		//手续费 = 总价格 * 系统手续费
		BigDecimal fee = totalPrice.multiply(new BigDecimal(Constant.BRICK_FEE));
		//净利润 = 买卖差值  - 手续费
		BigDecimal netProfit = hedging.getSellPrice().subtract(hedging.getBuyPrice()).abs().subtract(fee);
		//利润率
		BigDecimal profitMargin = netProfit.divide(totalPrice, 4, BigDecimal.ROUND_HALF_UP);

		log.info("买一价：{}，卖一价：{}, 买一交易所：{}， 卖一交易所：{}",hedging.getBuyPrice(), hedging.getSellPrice(), hedging.getBuyBourse(), hedging.getSellBourse());
		log.info("当前利润率：{}", profitMargin);
		//当前利润率
		brickList.forEach(brick -> {
			// log.info("当前策略需要利润率：{}", brick.getMinProfitMargin().toPlainString());
			//净率润大于设置的最小值才去搬砖
			if (profitMargin.compareTo(brick.getMinProfitMargin()) != -1) {
				log.info("开始查找支持交易所{}-{}的用户", hedging.getBuyBourse(), hedging.getSellBourse());
				//获取使用该策略的所有需要搬砖的用户  且是VIP
				List<UserBrick> userBrickList = userBrickService.findByBrickId(brick.getId());
				if (null != userBrickList && userBrickList.size() > 0) {
					for (UserBrick userBrick : userBrickList) {

						//验证用户是否支持当前两交易所的搬砖
						String[] bourseIdArray = userBrick.getBourseIds().split(",");
						List<String> bourseList = Arrays.asList(bourseIdArray);
						if (bourseList.indexOf(String.valueOf(hedging.getBuyBourse())) > -1 && bourseList.indexOf(String.valueOf(hedging.getSellBourse())) > -1) {
							User u = userService.getById(userBrick.getUserId());
							log.info("用户{}满足交易所{}-{}开始尝试下单", u.getUsername(), hedging.getSellBourse(), hedging.getBuyBourse());

							//支持给用户下单
							executorService.execute(() -> {

								// 查询USDT 余额
								BourseApi api = bourseApiService.getByUserBrick(userBrick.getId(), hedging.getSellBourse());
								BigDecimal usdtAmount = symbolSubService.getUSDTAmount(api);
								if (usdtAmount.compareTo(brick.getTradeNum().multiply(hedging.getSellPrice())) < 0) {
									log.error("用户{}的交易所{}-USDT余额不足:{}", u.getUsername(), hedging.getSellBourse(), usdtAmount);
									return;
								}

								long orderNo = sequence.nextId();

								BrickOrder sellOrder = new BrickOrder();
								sellOrder.setOrderNo(orderNo);
								sellOrder.setTradeType(TradeTypeEnum.SELL_MARKET.getValue());
								sellOrder.setBourse(hedging.getBuyBourse());
								sellOrder.setNum(brick.getTradeNum());
								sellOrder.setSymbol(symbol);
								sellOrder.setQuoteCurrency(quoteCurrency);
								sellOrder.setBaseCurrency(baseCurrency);
								sellOrder.setPrice(hedging.getBuyPrice());
								sellOrder.setTotalPrice(sellOrder.getPrice().multiply(sellOrder.getNum()));
								sellOrder.setStatus(0);
								sellOrder.setCreated(new Date());
								sellOrder.setBid(hedging.getBuyPrice());
								sellOrder.setData(hedging.getBuyData());
			        			sellOrder.setUsername(u.getUsername());
			        			sellOrder.setProfit(netProfit.multiply(brick.getTradeNum()));
								sellOrder.setProfitMargin(profitMargin);
			        			sellOrder.setBrickId(userBrick.getId());
								place(sellOrder);

								//市价买
								BrickOrder order = new BrickOrder();
								order.setOrderNo(orderNo);
								order.setTradeType(TradeTypeEnum.BUY_MARKET.getValue());
								order.setBourse(hedging.getSellBourse());
								order.setNum(brick.getTradeNum());
								order.setSymbol(symbol);
								order.setQuoteCurrency(quoteCurrency);
								order.setBaseCurrency(baseCurrency);
								order.setPrice(hedging.getSellPrice());
								order.setTotalPrice(order.getPrice().multiply(order.getNum()));
								order.setStatus(0);
								order.setCreated(new Date());
								order.setAsk(hedging.getSellPrice());
								order.setData(hedging.getSellData());
			        			order.setUsername(u.getUsername());
								order.setProfit(netProfit.multiply(brick.getTradeNum()));
								order.setBrickId(userBrick.getId());
								order.setProfitMargin(profitMargin);
								place(order);
							});
						} else {
							log.info("用户{}不满足交易所{}-{}下单，用户的交易所为：{}", userBrick.getUserId(), hedging.getSellBourse(), hedging.getBuyBourse(), userBrick.getBourseIds());
						}
					}
				}
			}
		});
	}

	@Override
	public Map<String, String> userTotalProfit(String username, Date start, Date end) {
		return baseMapper.userTotalProfit(username, start, end);
	}

	@Override
	public Page<BrickOrderVo> pageListByUserAndBrickId(Page<BrickOrderVo> page, String username, Long brickId) {
		return baseMapper.pageListByUserAndBrickId(page, username,brickId);
	}

	@Override
	public void cancelOrder(BrickOrder order) {
		BourseApi api = bourseApiService.getByUserBrick(order.getBrickId(), order.getBourse());
		switch (order.getBourse()) {
			case 1:
				HuobiUtil.cancelOrder(api, order);
				break;
			case 2:
				BinanceUtil.cancelOrder(api, order.getSymbol(), order.getOutId());
				break;
			case 3:
				OkexUtil.cancelOrder(api, order);
				break;
			case 4:
				if (BitforexUtil.cancelOrder(api, order)) {
					BitforexUtil.orderInfo(api, order);
				}
				break;
			default:
				throw new TransactionException("未知的交易所订单");
		}
	}

	@Override
	@Transactional
	public void orderHedging(String username, Long orderNo) {
		LambdaQueryWrapper<BrickOrder> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(BrickOrder::getOutId, orderNo);
		wrapper.eq(BrickOrder::getUsername, username);
		BrickOrder order = getOne(wrapper);
		if (!order.getStatus().equals(BrickOrder.Status.DEALED.value())) {
			throw new TransactionException("订单状态异常，无法对冲");
		}

		// 同步订单信息  进行对冲
		BrickOrder hedgingOrder = createHedgingOrder(order);
		place(hedgingOrder);
		order.setStatus(BrickOrder.Status.HEDGING.value());
		updateById(order);
    }

	/**
	 * 获取对冲订单
	 * @param order
	 * @return
	 */
	private BrickOrder createHedgingOrder (BrickOrder order) {
		// 反向买入
		int type = TradeTypeEnum.SELL_MARKET.getValue() == order.getTradeType()
				? TradeTypeEnum.BUY_MARKET.getValue() : TradeTypeEnum.SELL_MARKET.getValue();
		int i = 0;
		BigDecimal price = null;
		// 获取交易所价格
		while (i < 3) {
			BoursePrice boursePrice = symbolSubService.getPrice(order.getBourse(), order.getSymbol());
			if (i == TradeTypeEnum.SELL_MARKET.getValue()) {
				price = boursePrice.getBuyPrice();
			} else {
				price = boursePrice.getSellPrice();
			}

			if (price != null) {
				break;
			}
			i++;

			if (i == 3) {
				throw new TransactionException("当前交易所价格获取失败，稍后重试");
			}

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				throw new TransactionException("对冲错误，稍后重试");
			}
		}
		// 创建订单
		BrickOrder hedgingOrder = new BrickOrder();
		hedgingOrder.setOrderNo(order.getOrderNo());
		// 反向买入
		hedgingOrder.setTradeType(type);
		hedgingOrder.setBourse(order.getBourse());
		hedgingOrder.setNum(order.getNum());
		hedgingOrder.setSymbol(order.getSymbol());
		hedgingOrder.setQuoteCurrency(order.getQuoteCurrency());
		hedgingOrder.setBaseCurrency(order.getBaseCurrency());
		hedgingOrder.setPrice(price);
		hedgingOrder.setTotalPrice(hedgingOrder.getPrice().multiply(hedgingOrder.getNum()));
		hedgingOrder.setStatus(0);
		hedgingOrder.setCreated(new Date());
		hedgingOrder.setBid(price);
		hedgingOrder.setData(null);
		hedgingOrder.setUsername(order.getUsername());
		hedgingOrder.setProfit(null);
		hedgingOrder.setProfitMargin(null);
		hedgingOrder.setBrickId(null);
		return hedgingOrder;

	}
}
